package controller;

import filesystem.GlobalFSData;
import filesystem.model.Disk;
import filesystem.model.File;
import filesystem.service.DiskService;
import filesystem.service.FileService;
import filesystem.view.DiskPictureView;
import filesystem.view.FATTableView;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.chart.PieChart;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javafx.util.Callback;
import main.Main;
import util.UIError;


import java.io.IOException;
import java.net.URL;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ResourceBundle;

public class FileResourceManagerController extends Application implements Initializable {

    public static final String NAME = "Explorer";

    public static final Image FILE_ICON = new Image("/imgs/file.png",16,16,true,true);
    public static final Image DIRECTORY_ICON = new Image("/imgs/dir.png",16,16,true,true);

    public static final String RENAME_FAILED = "重命名失败";
    public static final String CREATE_FAILED = "创建失败";
    public static final String DELETE_FAILED = "删除失败";


    @FXML
    private ImageView backToParent;

    @FXML
    private FlowPane flowPane;

    @FXML
    private VBox filesystemScene;

        // menu bar edit
    @FXML
    private Menu menuBarEdit;

    @FXML
    private MenuItem menuBarEditOpen;

    @FXML
    private MenuItem menuBarEditRemove;

    // tool bar
    @FXML
    private ToolBar toolBar;

    @FXML
    private Button toolBarButtonTerminal;

    @FXML
    private Button toolBarButtonProperty;

    // src.main pane
    @FXML
    private SplitPane mainPane;

    @FXML
    private ScrollPane treeViewScrollPane;

        // tree view
    @FXML
    private TreeView<File> treeView;

            // tree view popup menu
    @FXML
    private ContextMenu treeViewPopupMenu;

    @FXML
    private MenuItem treeViewPopupMenuCreateFile;

    @FXML
    private MenuItem treeViewPopupMenuCreateDirectory;

    @FXML
    private MenuItem fileViewPopupMenuOpenSelected;

    @FXML
    private MenuItem fileViewPopupMenuRemoveSelected;

    // imformation view
    @FXML
    private Accordion infomationStack;


    // variable
    private Stage primaryStage;
    public File current;
    public File selected;

    private Stage diskStage;
    private Stage fatStage;

    @FXML
    void createDirectory(ActionEvent event) {
        try {
            FileService.createNew(this.current, File.DIRECTORY);
            updateDiskAndFat();
        } catch (IOException e) {
            UIError.alertInformation(FileResourceManagerController.CREATE_FAILED, e.getMessage(), FileResourceManagerController.this.primaryStage);
        }
        this.updateAll();
    }

    @FXML
    void createFile(ActionEvent event) {
        try {
            FileService.createNew(this.current, File.FILE);
            updateDiskAndFat();
        } catch (IOException e) {
            UIError.alertInformation(FileResourceManagerController.CREATE_FAILED, e.getMessage(), FileResourceManagerController.this.primaryStage);
        }
        this.updateAll();
    }

    @FXML
    void property(ActionEvent event) {
        AttributeController a = new AttributeController(this.selected);
        a.start(new Stage());
    }

    @FXML
    void exit(ActionEvent event) {
//    	System.out.println(AttrForFS.getDisk().getDiskTable());
        this.primaryStage.close();
    }

    @FXML
    void launchTerminal(ActionEvent event) {
        Terminal t = new Terminal(this.current);
        t.start(new Stage());
    }

    @FXML
    void openSelected(ActionEvent event) {
        open(this.selected);

    }

    @FXML
    void removeSelected(ActionEvent event) {
        try {
            if (this.selected.isFile()) {
                FileService.removeFile(this.selected);
            } else if (this.selected.isDirectory()) {
                FileService.removeDir(this.selected);
            }

            this.selected = null;
        } catch (IOException e) {
            UIError.alertInformation(FileResourceManagerController.DELETE_FAILED, e.getMessage(), FileResourceManagerController.this.primaryStage);
        }
        updateDiskAndFat();
        this.updateAll();
    }

    @FXML
    void switchToParent(MouseEvent event) {
        this.switchDirectory(this.current.getParentFile());
    }

    public void switchDirectory(File f) {
        if (f != null && f.isDirectory()) {
            this.current = f;
            refreshPicture();
        }
        this.updateButtonStatus();
        this.primaryStage.setTitle(this.current.getNormalName()+" - "+ FileResourceManagerController.NAME);
    }

    public void open(File f) {
        if (f.isFile()) {
            (new EditorController(f)).start(new Stage());
        } else if (f.isDirectory()) {
            this.switchDirectory(f);
        }
    }

    public File rename(File file, String name) {
        if (file.getAttribute() == File.ROOT || file.getName().equals(name)) {
            return file;
        }
        try {
            return FileService.rename(file, name);
        } catch (IOException e) {
            UIError.alertInformation(FileResourceManagerController.RENAME_FAILED, e.getMessage(), FileResourceManagerController.this.primaryStage);
            return file;
        }
    }

    public void updateAll() {
        this.updateTreeView();
        this.updateFileView();
//        ((DiskViewer) this.diskChart.getContent()).update();
//        ((FATViewer) this.FATTable.getContent()).update();
        this.updateButtonStatus();
    }

    public void updateButtonStatus() {

        if (this.selected == null) {
            this.menuBarEdit.setDisable(true);
            this.menuBarEditOpen.setDisable(true);
            this.menuBarEditRemove.setDisable(true);
            this.toolBarButtonProperty.setDisable(true);
            this.fileViewPopupMenuOpenSelected.setDisable(true);
            this.fileViewPopupMenuRemoveSelected.setDisable(true);
        } else {
            this.menuBarEdit.setDisable(false);
            this.menuBarEditOpen.setDisable(false);
            this.menuBarEditRemove.setDisable(false);
            this.toolBarButtonProperty.setDisable(false);
            this.fileViewPopupMenuOpenSelected.setDisable(false);
            this.fileViewPopupMenuRemoveSelected.setDisable(false);
        }
    }

    public void updateTreeView() {
        this.treeView.setRoot(generateTreeItem(GlobalFSData.getRoot()));
    }

    public void updateFileView() {
        refreshPicture();
    }

    private TreeItem<File> generateTreeItem(final File f) {
        TreeItem<File> treeItem = new TreeItem<File>(f) {

            private boolean isLeaf = false;
            private boolean isFirstTimeChildren = true;
            private boolean isFirstTimeLeaf = true;

            @Override
            public ObservableList<TreeItem<File>> getChildren() {
                if (isFirstTimeChildren) {
                    isFirstTimeChildren = false;
                    super.getChildren().setAll(buildChildren(this));
                }
                return super.getChildren();
            }

            @Override
            public boolean isLeaf() {
                if (isFirstTimeLeaf) {
                    isFirstTimeLeaf = false;
                    File file = (File) this.getValue();
                    if (file.isDirectory()) {
                        List<Object> files = FileService.getSubFiles(file);
                        this.isLeaf = files.isEmpty();
                    } else {
                        this.isLeaf = true;
                    }
                }
                return this.isLeaf;
            }

            private ObservableList<TreeItem<File>> buildChildren(TreeItem<File> i) {
                File file = i.getValue();
                if (file == null) {
                    return FXCollections.emptyObservableList();
                } else if (file.isDirectory()) {
                    List<Object> files = FileService.getSubFiles(file);
                    if (!files.isEmpty()) {
                        ObservableList<TreeItem<File>> children = FXCollections.observableArrayList();
                        for (Object childFile : files) {
                            File c = (File) childFile;
                            if (c.isDirectory()) {
                                children.add(generateTreeItem(c));
                            }
                        }
                        return children;
                    }
                    return FXCollections.emptyObservableList();
                } else {
                    return FXCollections.emptyObservableList();
                }
            }
        };
        treeItem.setExpanded(true);
        treeItem.setGraphic(new ImageView(FileResourceManagerController.DIRECTORY_ICON));
        return treeItem;
    }

    private List<FileModelTableItem> getPaneList(File file){
        List<FileModelTableItem> list = new ArrayList<>();
        for(Object o : FileService.getSubFiles(file)){
            if(o instanceof File){
                list.add(new FileModelTableItem((File) o));
            }
        }
        return list;
    }

    public class FileModelTableItem extends Label{
        public File file;
        public Node icon;


        public FileModelTableItem get(){
            return this;
        }

        public FileModelTableItem(File file) {
            this.file = file;
            if (this.file.isFile()) {
                ImageView view = new ImageView(FileResourceManagerController.FILE_ICON);
                this.icon = view;

            } else if (this.file.isDirectory()) {
                ImageView view = new ImageView(FileResourceManagerController.DIRECTORY_ICON);
                this.icon = view;
            }
            initData();
        }

        private void initData(){
            this.setGraphicTextGap(10);
            this.setPadding(new Insets(1, 1, 1, 1));
            this.setContentDisplay(ContentDisplay.TOP);
            this.setPrefSize(90,90);
            this.setText(this.file.getName());
            ImageView imageView = new ImageView();
            Image image = null;
            if(this.file.isFile()){
                image = new Image("/imgs/file.png",90,90,true,true);
                imageView.setImage(image);
            }
            else if(this.file.isDirectory()){
                image = new Image("/imgs/dir.png",80,80,true,true);
                imageView.setImage(image);
            }
            this.setGraphic(imageView);
            addListener();
        }


        public void addListener(){
            //添加鼠标监听器，双击打开文件或文件夹
            this.setOnMouseClicked((MouseEvent e) -> {
                if(e.getClickCount() == 1){
                    //清除已选择的图片
                    ObservableList<Node> children = flowPane.getChildren();
                    for(Node node : children){
                        node.setStyle("-fx-background-color:transparent");
                    }
                    selected = this.file;
                    //判断selected是否为空如果为空清除
                    this.setStyle("-fx-background-color:POWDERBLUE");
                    updateButtonStatus();
                }
                else if(e.getClickCount() == 2){
                    File f = file;
                    if (f != null) {
                        if (f.isFile()) {
                            (new EditorController(f)).start(new Stage());
                        } else if (f.isDirectory()) {
                            FileResourceManagerController.this.switchDirectory(f);
                        }
                    }
                }
            });
            this.setOnMouseEntered((MouseEvent e) -> {
                if (!this.file.equals(selected))
                    this.setStyle("-fx-background-color:POWDERBLUE");

            });
            this.setOnMouseExited((MouseEvent e) -> {
                if (!this.file.equals(selected))
                    this.setStyle("-fx-background-color:transparent;");

            });
        }
        public File getFile() {return this.file;}
        public Node getIcon() {return this.icon;}
        public String getName() {return this.file.getNormalName();}
        public String getType() {
            if (this.file.isFile()) {
                return "文件";
            } else if (this.file.isDirectory()) {
                return "目录";
            } else {
                return "?";
            }
        }
        public Integer getSize() {return this.file.getSize();}
        public String toString() {return this.file.toString();}
    }


    @Override
    public void initialize(URL location, ResourceBundle resources) {
        // configuring tree view style
        this.treeView.setCellFactory(new Callback<TreeView<File>, TreeCell<File>>() {
            @Override
            public TreeCell<File> call(TreeView<File> param) {
                TreeCell<File> c = new TreeCell<File>() {
                    private TextField tf;
                    @Override
                    public void startEdit() {
                        super.startEdit();
                        if (this.tf == null) {
                            createTextField();
                        }
                        this.setText(null);
                        this.setGraphic(this.tf);
                        this.tf.setText(this.getTreeItem().getValue().getNormalName());
                        this.tf.selectAll();
                        this.tf.requestFocus();
                    }

                    @Override
                    public void cancelEdit() {
                        System.out.println("cancel edit");
                        super.cancelEdit();
                        this.setText(this.getTreeItem().getValue().getName());
                        this.setGraphic(this.getTreeItem().getGraphic());
                    }

                    @Override
                    protected void updateItem(File f, boolean empty) {
                        super.updateItem(f, empty);
                        if (empty) {
                            this.setText(null);
                            this.setGraphic(null);
                        } else {
                            if (isEditing()) {
                                if (this.tf != null) {
                                    this.tf.setText(this.getTreeItem().getValue().getName());
                                }
                            } else {
                                this.setText(this.getTreeItem().getValue().getName());
                                this.setGraphic(this.getTreeItem().getGraphic());
                            }
                        }
                    }

                    public void createTextField() {
                        this.tf = new TextField(this.getTreeItem().getValue().getName());
                        final File file = this.getItem();
                        this.tf.setOnKeyReleased(new EventHandler<KeyEvent>() {
                            @Override
                            public void handle(KeyEvent event) {
                                if (event.getCode() == KeyCode.ENTER) {
                                    commitEdit(rename(file, tf.getText()));
                                } else if (event.getCode() == KeyCode.ESCAPE) {
                                    cancelEdit();
                                }
                            }
                        });
                        this.tf.focusedProperty().addListener(new ChangeListener<Boolean>() {
                            @Override
                            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
                                if (oldValue && (!newValue)) {
                                    commitEdit(rename(file, tf.getText()));
                                }
                            }
                        });
                    }
                };

                //在这里设置监听，更改文件夹
                c.setOnMouseClicked(new EventHandler<MouseEvent>() {
                    @Override
                    public void handle(MouseEvent event) {
                        FileResourceManagerController.this.switchDirectory(c.getItem());

                    }
                });
                return c;
            }
        });
        flowPane.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                //如果点击的pane，取消select
                if(event.getTarget() instanceof FlowPane){
                    selected = null;
                    updateAll();
                }
            }
        });
        this.diskStage = new Stage();
        this.fatStage = new Stage();
        // 初始化组件
        backToParent.setImage(new Image("/imgs/backTo.png",40,40,true,true));
        switchDirectory(GlobalFSData.getRoot());
        this.updateTreeView();
        this.updateButtonStatus();
    }

    @Override
    public void start(Stage primaryStage) {
        this.primaryStage = primaryStage;
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/explorer.fxml"));
        loader.setController(this);
        try {
            loader.load();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Scene scene = new Scene(this.filesystemScene);
        primaryStage.setScene(scene);
        primaryStage.setResizable(true);
        primaryStage.setTitle("资源管理器");
        primaryStage.show();
        primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
            public void handle(WindowEvent windowEvent) {
                //每次关闭窗口的时候，就把对象的二进制流写入文件里面
                DiskService.save2Disk(GlobalFSData.getDisk(), Main.DISK, GlobalFSData.getFat());
                primaryStage.close();
            }
        }); // save file system if clicking the close button
    }

    @FXML
    void refreshPicture() {
        List<FileModelTableItem> fileModelTableItems = getPaneList(this.current);
        flowPane.getChildren().remove(0,flowPane.getChildren().size());
        for(FileModelTableItem file : fileModelTableItems){
            flowPane.getChildren().add(file);
        }
    }

    @FXML
    void openDiskChart(ActionEvent event) {
        Disk disk = GlobalFSData.getDisk();
        int[] table = disk.getFat().getTable();

        //用一个数字记录磁盘中被占用的块
        double count = 0;
        for(int i = 0;i < table.length;i++){
            if(table[i] == -1){
                count++;
            }
        }
        ObservableList<PieChart.Data> chartData = FXCollections.observableArrayList();
        DecimalFormat df  =new  DecimalFormat("#.00");
        chartData.addAll(new PieChart.Data("已用空间"+df.format(100*count/256)+"%", count),
                new PieChart.Data("可用空间"+df.format(100*(256-count)/256)+"%", 256-count));
        PieChart pieChart = new PieChart();
        pieChart.setData(chartData);
        pieChart.setLabelsVisible(false);
        diskStage.setTitle("磁盘占用图");
        StackPane root = new StackPane();
        root.getChildren().addAll(pieChart);

        HBox hBox = new HBox();
        hBox.getChildren().addAll(root,new Separator(),new DiskPictureView(GlobalFSData.getDisk()));
        Scene scene = new Scene(hBox, 900, 400);
        diskStage.setScene(scene);
        diskStage.show();
        List<String> colors = Arrays.asList("lightgreen","lightskyblue");
        int i = 0;
//        for(PieChart.Data data:chartData){
//            data.getNode().setStyle("-fx-pie-color: "+colors.get(i % 2)+";");
//            i++;
//        }
    }

    @FXML
    void openFAT(ActionEvent event) {
        Scene scene = new Scene(new FATTableView(GlobalFSData.getFat()), 400, 400);
        fatStage.setScene(scene);
        fatStage.show();
    }

    void updateDiskAndFat(){
        Disk disk = GlobalFSData.getDisk();
        int[] table = disk.getFat().getTable();

        //用一个数字记录磁盘中被占用的块
        double count = 0;
        for(int i = 0;i < table.length;i++){
            if(table[i] == -1){
                count++;
            }
        }
        ObservableList<PieChart.Data> chartData = FXCollections.observableArrayList();
        DecimalFormat df  =new  DecimalFormat("#.00");
        chartData.addAll(new PieChart.Data("已用空间"+df.format(100*count/256)+"%", count),
                new PieChart.Data("可用空间"+df.format(100*(256-count)/256)+"%", 256-count));
        Label caption = new Label("");
        caption.setTextFill(Color.DARKGRAY);
        caption.setStyle("-fx-font: 24 arial");
        PieChart pieChart = new PieChart();
        pieChart.setLabelsVisible(false);
        pieChart.setData(chartData);
        diskStage.setTitle("磁盘占用图");
        StackPane root = new StackPane();
        root.getChildren().addAll(pieChart,caption);

        HBox hBox = new HBox();
        hBox.getChildren().addAll(root,new Separator(),new DiskPictureView(GlobalFSData.getDisk()));
        Scene s1 = new Scene(hBox, 900, 400);
        List<String> colors = Arrays.asList("lightgreen","lightskyblue");
        int i = 0;
        diskStage.setScene(s1);
        Scene s2 = new Scene(new FATTableView(GlobalFSData.getFat()), 400, 400);
        fatStage.setScene(s2);
    }
}
